<?php
/**************************************************************************
 *
 *   Copyright 2010 American Public Media Group
 *
 *   This file is part of AIR2.
 *
 *   AIR2 is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   AIR2 is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with AIR2.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************/

/**
 * Project
 *
 * Provides way to group inquiries and collaborate between organizations
 *
 * @property integer $prj_id
 * @property string $prj_uuid
 * @property string $prj_name
 * @property string $prj_display_name
 * @property string $prj_desc
 * @property string $prj_status
 * @property string $prj_type
 * @property boolean $prj_shared_flag
 * @property integer $prj_cre_user
 * @property integer $prj_upd_user
 * @property timestamp $prj_cre_dtim
 * @property timestamp $prj_upd_dtim
 * @property Doctrine_Collection $ProjectActivity
 * @property Doctrine_Collection $ProjectAnnotation
 * @property Doctrine_Collection $ProjectInquiry
 * @property Doctrine_Collection $ProjectMessage
 * @property Doctrine_Collection $ProjectOrg
 * @property Doctrine_Collection $ProjectOutcome
 * @property Doctrine_Collection $SrcActivity
 * @property Doctrine_Collection $OrgDefault
 * @author rcavis
 * @package default
 */
class Project extends AIR2_Record {
    /* code_master values */
    public static $STATUS_ACTIVE = 'A';
    public static $STATUS_INACTIVE = 'F';
    public static $TYPE_INQUIRY = 'I';
    public static $TYPE_SYSTEM = 'S';


    /**
     * Set the table columns
     */
    public function setTableDefinition() {
        $this->setTableName('project');
        $this->hasColumn('prj_id', 'integer', 4, array(
                'primary' => true,
                'autoincrement' => true,
            ));
        $this->hasColumn('prj_uuid', 'string', 12, array(
                'fixed' => true,
                'notnull' => true,
                'unique' => true,
            ));
        $this->hasColumn('prj_name', 'string', 32, array(
                'notnull' => true,
                'unique'  => true,
                'airvalid' => array(
                    '/^[a-zA-Z0-9\-\_]+$/' => 'Invalid character(s)! Use [A-Za-z0-9], dashes and underscores',
                ),
            ));
        $this->hasColumn('prj_display_name', 'string', 255, array(
                'notnull' => true,
            ));
        $this->hasColumn('prj_desc', 'string', null, array(

            ));
        $this->hasColumn('prj_status', 'string', 1, array(
                'fixed' => true,
                'notnull' => true,
                'default' => self::$STATUS_ACTIVE,
            ));
        $this->hasColumn('prj_type', 'string', 1, array(
                'fixed' => true,
                'notnull' => true,
                'default' => self::$TYPE_INQUIRY,
            ));
        $this->hasColumn('prj_shared_flag', 'boolean', null, array(
                'notnull' => true,
                'default' => false,
            ));
        $this->hasColumn('prj_cre_user', 'integer', 4, array(
                'notnull' => true,
            ));
        $this->hasColumn('prj_upd_user', 'integer', 4, array(

            ));
        $this->hasColumn('prj_cre_dtim', 'timestamp', null, array(
                'notnull' => true,
            ));
        $this->hasColumn('prj_upd_dtim', 'timestamp', null, array(

            ));

        parent::setTableDefinition();
    }


    /**
     * Set table relations
     */
    public function setUp() {
        parent::setUp();
        $this->hasMany('ProjectActivity', array(
                'local' => 'prj_id',
                'foreign' => 'pa_prj_id'
            ));
        $this->hasMany('ProjectAnnotation', array(
                'local' => 'prj_id',
                'foreign' => 'prjan_prj_id'
            ));
        $this->hasMany('ProjectInquiry', array(
                'local' => 'prj_id',
                'foreign' => 'pinq_prj_id'
            ));
        $this->hasMany('ProjectMessage', array(
                'local' => 'prj_id',
                'foreign' => 'pm_pj_id'
            ));
        $this->hasMany('ProjectOrg', array(
                'local' => 'prj_id',
                'foreign' => 'porg_prj_id'
            ));
        $this->hasMany('ProjectOutcome', array(
                'local' => 'prj_id',
                'foreign' => 'prjo_prj_id'
            ));
        $this->hasMany('ProjectBatch as Batches', array(
                'local'   => 'prj_id',
                'foreign' => 'pb_prj_id',
            )
        );
        $this->hasMany('SrcActivity', array(
                'local' => 'prj_id',
                'foreign' => 'sact_prj_id'
            ));

        // Tagging
        $this->hasMany('TagProject as Tags', array(
                'local' => 'prj_id',
                'foreign' => 'tag_xid'
            )
        );

        // Organization this project is default for (optional)
        $this->hasMany('Organization as OrgDefault', array(
                'local' => 'prj_id',
                'foreign' => 'org_default_prj_id'
            )
        );
    }


    /**
     *
     *
     * @param array   $orgs
     */
    public function add_orgs(array $orgs) {
        foreach ($orgs as $org) {
            $po = new ProjectOrg();
            $po->porg_org_id    = $org->org_id;
            $po->porg_status    = ProjectOrg::$STATUS_ACTIVE;
            // get the first related user who can writer and make them the contact.
            foreach ($org->UserOrg as $uo) {
                if ($uo->AdminRole->ar_code == 'W'
                    || $uo->AdminRole->ar_code == 'M'
                ) {
                    $po->porg_contact_user_id = $uo->uo_user_id;
                }
            }
            $this->ProjectOrg[] = $po;
        }
    }


    /**
     * Returns array of related Organization org_ids that affect
     * the authorization of the Project. Only active Organizations
     * are included, and all children and parent Organizations of explicitly
     * assigned ProjectOrg records are included.
     *
     * @return array $org_ids
     */
    public function get_authz() {
        $org_ids = array();
        foreach ($this->ProjectOrg as $porg) {
            if ($porg->porg_status == ProjectOrg::$STATUS_ACTIVE) {
                $this->_get_related_org_ids($org_ids, $porg->Organization);
            }
        }
        return $org_ids;
    }


    /**
     *
     *
     * @param array   $org_ids (reference)
     * @param Organization $org
     */
    private function _get_related_org_ids(&$org_ids, $org) {
        if ($org->org_status == Organization::$STATUS_ACTIVE) {
            $org_ids[] = $org->org_id;
            foreach ($org->get_active_parents_ids() as $oid) {
                $org_ids[] = $oid;
            }
            foreach ($org->children as $o) {
                $this->_get_related_org_ids($org_ids, $o);
            }
        }
    }


    /**
     *
     *
     * @param User    $user
     * @return authz integer
     */
    public function user_may_read($user) {
        if ($user->is_system()) {
            return AIR2_AUTHZ_IS_SYSTEM;
        }

        if ($this->prj_shared_flag) {
            return AIR2_AUTHZ_IS_PUBLIC;
        }

        $prj_org_ids = $this->get_authz();
        $user_org_ids = array_keys($user->get_authz());
        if (count(array_intersect($prj_org_ids, $user_org_ids))) {
            return AIR2_AUTHZ_IS_ORG;
        }

        return AIR2_AUTHZ_IS_DENIED;
    }


    /**
     *
     *
     * @param User    $user
     * @return authz integer
     */
    public function user_may_write($user) {
        if ($user->is_system()) {
            return AIR2_AUTHZ_IS_SYSTEM;
        }

        // abort early if cannot read
        if (!$this->user_may_read($user)) {
            return AIR2_AUTHZ_IS_DENIED;
        }
        // if the user has WRITER role in any related organization, may write
        $user_authz = $user->get_authz();
        $prj_org_ids = $this->get_authz();
        //diag_dump( $user_authz );
        //diag_dump( $prj_org_ids );
        foreach ($prj_org_ids as $org_id) {
            if (isset($user_authz[$org_id])
                && (AIR2_AUTHZ_WRITER & $user_authz[$org_id])
            ) {
                return AIR2_AUTHZ_IS_ORG;
            }
        }

        return AIR2_AUTHZ_IS_DENIED;
    }


    /**
     *
     *
     * @param User    $user
     * @return authz integer
     */
    public function user_may_manage($user) {
        if ($user->is_system()) {
            return AIR2_AUTHZ_IS_SYSTEM;
        }

        foreach ($this->ProjectOrg as $porg) {
            if ($porg->porg_contact_user_id == $user->user_id) {
                return AIR2_AUTHZ_IS_MANAGER;
            }
        }

        return AIR2_AUTHZ_IS_DENIED;
    }


    /**
     * Apply authz rules for who may view the existence of a Project.
     *
     * @param AIR2_Query $q     (reference)
     * @param User    $u     (reference)
     * @param string  $alias (optional)
     */
    public static function query_may_read(AIR2_Query &$q, User &$u, $alias=null) {
        if ($u->is_system()) {
            return;
        }
        $a = ($alias) ? "$alias." : "";

        // is current user in an org related to this project
        $readable_org_ids = $u->get_authz_str(AIR2_AUTHZ_READER, 'porg_org_id');
        $po_act = ProjectOrg::$STATUS_ACTIVE;
        $member_prjs = "select porg_prj_id from project_org where porg_status = '$po_act'".
            " and $readable_org_ids";
        $q->addWhere("(${a}prj_id in ($member_prjs) or ${a}prj_shared_flag = 1)");
    }


    /**
     * Apply authz rules for who may write to a Project.
     *
     * @param AIR2_Query $q     (reference)
     * @param User    $u     (reference)
     * @param string  $alias (optional)
     */
    public static function query_may_write(AIR2_Query &$q, User &$u, $alias=null) {
        if ($u->is_system()) {
            return;
        }

        $a = ($alias) ? "$alias." : "";
        $user_id = $u->user_id;

        // TODO would there ever be a case where the owner was not a WRITER in a related org?
        // not if user_may_write() is always used before save().
        $org_str = $u->get_authz_str(AIR2_AUTHZ_WRITER, 'po2.porg_org_id');

        // user is an owner (manager) of project
        self::query_may_manage($q, $u, $alias);

        // or user is a WRITER in a related org
        $has_writer_role = "${a}prj_id IN (SELECT po2.porg_prj_id FROM ProjectOrg po2 WHERE po2.porg_status = ? AND $org_str)";
        $q->orWhere($has_writer_role, array(ProjectOrg::$STATUS_ACTIVE));

        //diag($q->getSqlQuery());

    }


    /**
     * Apply authz rules for who may manage a Project.
     *
     * @param AIR2_Query $q     (reference)
     * @param User    $u     (reference)
     * @param string  $alias (optional)
     */
    public static function query_may_manage(AIR2_Query &$q, User &$u, $alias=null) {
        if ($u->is_system()) {
            return;
        }

        $a = ($alias) ? "$alias." : "";
        $user_id = $u->user_id;

        // user is an owner (manager) of project
        $is_owner = "${a}prj_id IN " .
            "(SELECT po1.porg_prj_id FROM ProjectOrg po1 WHERE po1.porg_status = ? AND po1.porg_contact_user_id = ?)";
        $q->addWhere($is_owner, array(ProjectOrg::$STATUS_ACTIVE, $user_id));

    }


}
